//深度遍历ast
//新增一个context，为后续埋伏笔！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
function traverserNode(ast, context) {
    //直接修改原始数据？？感觉不大好
    context.currentNode = ast;
    //添加退回阶段的回调函数数组
    const exitFns = [];
    //context.nodeTransforms是一个数组，其中的每一个元素都是一个函数
    const transforms = context.nodeTransforms;
    //把原来的具体操作内容抽象出去
    for (let i = 0; i < transforms.length; i++) {
        const onExit = transforms[i](context.currentNode, context);
        if (onExit) {
            //将推出阶段你的回调函数添加到exitFns数组中
            exitFns.push(onExit);
        }
        //由于任何转换函数都可以移除当前节点，因此每个执行函数
        //检查当前简单是否已经移除，如果移除，直接中断for
        if (!context.currentNode) {
            return;
        }
    }

    //如果有子节点，则递归地调用traverseNode函数进行遍历
    const children = context.currentNode.children;
    if (children) {
        for (let i = 0; i < children.length; i++) {
            //在转换之前，将单节点设置为父节点
            context.parent = context.currentNode;
            context.childIndex = i;
            traverserNode(children[i], context);
        }
    }

    //在节点处理的最后阶段执行缓存到exitFns中的回调函数
    let i = exitFns.length;
    while (i--) {
        exitFns[i]();
    }
}

//一些辅助函数================
//创建Stringliteral节点
function CreateStringLiteral(value) {
    return {
        type: "StringLiteral",
        value,
    };
}
//穿件Identifier节点
function createIdentifier(name) {
    return {
        type: "Identifier",
        name,
    };
}
//用来创建ArrayExpression 节点
function createArrayExpression(elements) {
    return {
        type: "ArrayExpression",
        elements,
    };
}
//用来创建CallExpression节点
function createCallExpression(callee, arguments) {
    return {
        type: "CallExpression",
        callee: createIdentifier(callee),
        arguments,
    };
}

function transformA(node, context) {
    ///转换的程序
    console.log("transformA 进入阶段======");
    return () => {
        //退出阶段
        console.log("transformA 退出阶段======");
    };
}

function transformText(node) {
    if (node.type != "Text") {
        return;
    }
    /**
     *
     * 文本节点对应的javascript AST 节点其实就是一个字符串字面量
     * 因此只需要使用node.content 创建一个StringLiteral 类型的节点即可
     * 最后将文本节点对应的javascript AST 节点添加到node.jsNode属性下
     */
    node.jsNode = CreateStringLiteral(node.content);
}

function transformElement(node) {
    /**
     * 将转换地面编写在推出阶段的回调函数中
     * 这样可以保证该标签的节点的子节点全部被处理完毕
     */
    return () => {
        if (node.type !== "Element") {
            return;
        }
        /**
         * 1.创建h函数调用语句
         * h 函数调用的第一个参数是标签名称，因此以node.tag来创建一个字符串字面量节点
         * 作为第一个参数
         */
        const callExp = createCallExpression("h", [CreateStringLiteral(node.tag)]);
        //2.处理h函数的参数
        node.children.length === 1 ?
            callExp.arguments.push(node.children[0].jsNode) :
            callExp.arguments.push(
                createArrayExpression(node.children.map((c) => c.jsNode))
            );
        //3.将当前表情节点对应的javascript AST添加到jsnode属性下
        node.jsNode = callExp;
    };
}
//转换ROOT根节点
function transformRoot(node) {
    //将逻辑卸载退回阶段函数中，保证子节点全部被处理完毕
    return () => {
        //如果不是根节点，则什么都不做
        if (node.type !== "Root") {
            return;
        }
        /**
         * node 是根节点，更节点的第一个子节点就是模板的根节点
         * 当然，这里暂时不考虑模板存在多个根节点的情况
         */
        const vnodeJSAST = node.children[0].jsNode;
        //创建render函数的声明语句的节点，将vnodeJSAST作为render函数体的返回体
        node.jsNode = {
            type: "FunctionDecl",
            id: { type: "Identifier", name: "render" },
            params: [],
            body: [{
                type: "ReturnStatement",
                return: vnodeJSAST,
            }, ],
        };
    };
}

//封装transform函数，来对AST进行转换
function transform(ast) {
    const context = {
        //添加currentNode，用来存储当前正在转换的节点
        currentNode: null,
        childIndex: 0,
        parent: null,
        nodeTransforms: [transformRoot, transformElement, transformText],
        //用来替换节点的函数，接受新的节点座位参数
        replaceNode(node) {
            /**
             * 为了替换跌点我们需要修改AST
             * 找到当前节点在父节点的children中的位置，context.childIndex
             * 然后使用新节点替代
             */
            context.parent.children[context.childIndex] = node;
            //由于当前节点呗新的节点体寒掉了，因此我们需要将currentNode更新为新节点
            context.currentNode = node;
        },
        //用于删除当前节点
        removeNode() {
            console.log("remove", context.parent, context.childIndex);
            if (context.parent) {
                //调用数组的slice方法，根据当前节点的索引删除当前节点
                context.parent.children.splice(context.childIndex, 1);
                console.log(" context.parent.children", context.parent.children);
                //将context.currentNode置空
                context.currentNode = null;
            }
        },
    };
    traverserNode(ast, context);
    return ast.jsNode;
}